using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;

namespace NBear.Web.UI
{
    public delegate object[] GetRootItemsHandler();

    public delegate object[] GetNonRootItemsHandler(string parentId);

    public delegate object GetItemHandler(string id);

    public delegate string GetItemIdHandler(object obj);

    public class HierarchyData : System.Web.UI.IHierarchyData
    {
        #region Private Members

        private object obj;
        private GetItemIdHandler itemIdHandler;
        private GetNonRootItemsHandler handler;
        private object parentObj;
        private string parentPath;
        private object[] childObjs;

        #endregion

        #region Constructors

        public HierarchyData(object parentObj, string parentPath, object obj, 
            GetItemIdHandler itemIdHandler, GetNonRootItemsHandler handler)
        {
            this.parentObj = parentObj;
            this.parentPath = parentPath;
            this.obj = obj;
            this.itemIdHandler = itemIdHandler;
            this.handler = handler;
            childObjs = handler(itemIdHandler(obj));
        }

        #endregion

        #region IHierarchyData Members

        public System.Web.UI.IHierarchicalEnumerable GetChildren()
        {
            return new HierarchicalCollection(parentObj, parentPath, handler(itemIdHandler(obj)), itemIdHandler, handler);
        }

        public System.Web.UI.IHierarchyData GetParent()
        {
            string[] pathSplits = parentPath.Trim(' ', '/').Split('/');
            if (pathSplits != null && pathSplits.Length > 0)
            {
                if (pathSplits.Length > 1)
                {
                    string parentParentPath = "";
                    for (int i = 0; i < pathSplits.Length - 1; i++)
                    {
                        parentParentPath = parentParentPath + "/" + pathSplits[i];
                    }
                    return new HierarchyData(itemIdHandler(pathSplits[pathSplits.Length - 2]), 
                        parentParentPath, parentObj, itemIdHandler, handler);
                }
            }
            return null;
        }

        public bool HasChildren
        {
            get
            {
                return childObjs != null && childObjs.Length > 0;
            }
        }

        public object Item
        {
            get
            {
                return obj;
            }
        }

        public string Path
        {
            get
            {
                return string.Format("{0}/{1}", parentPath, itemIdHandler(obj));
            }
        }

        public string Type
        {
            get
            {
                return obj.GetType().ToString();
            }
        }

        #endregion
    }

    public class HierarchicalCollection : System.Web.UI.IHierarchicalEnumerable
    {
        #region Private Members

        private object parentObj;
        private string parentPath;
        private object[] objs;
        private GetItemIdHandler itemIdHandler;
        private GetNonRootItemsHandler nonRootHandler;

        #endregion
        
        #region Constructors

        public HierarchicalCollection(object parentObj, string parentPath, object[] objs, 
            GetItemIdHandler itemIdHandler, GetNonRootItemsHandler nonRootHandler)
        {
            this.parentObj = parentObj;
            this.parentPath = parentPath;
            this.objs = objs;
            this.itemIdHandler = itemIdHandler;
            this.nonRootHandler = nonRootHandler;
        }

        #endregion

        #region IHierarchicalEnumerable Members

        public System.Web.UI.IHierarchyData GetHierarchyData(object enumeratedItem)
        {
            return new HierarchyData(parentObj, parentPath, enumeratedItem, itemIdHandler, nonRootHandler);
        }

        #endregion

        #region IEnumerable Members

        public System.Collections.IEnumerator GetEnumerator()
        {
            return objs.GetEnumerator();
        }

        #endregion
    }

    public class RootHierarchicalDataSourceView : System.Web.UI.HierarchicalDataSourceView
    {
        #region Private Members

        private GetItemIdHandler itemIdHandler;
        private GetRootItemsHandler rootHandler;
        private GetNonRootItemsHandler nonRootHandler;

        #endregion

        #region Constructors

        public RootHierarchicalDataSourceView(GetItemIdHandler itemIdHandler, 
            GetRootItemsHandler rootHandler, GetNonRootItemsHandler nonRootHandler)
        {
            this.itemIdHandler = itemIdHandler;
            this.rootHandler = rootHandler;
            this.nonRootHandler = nonRootHandler;
        }

        #endregion

        #region Overriden Members

        public override System.Web.UI.IHierarchicalEnumerable Select()
        {
            return new HierarchicalCollection(null, null, rootHandler(), itemIdHandler, nonRootHandler);
        }

        #endregion
    }

    public class NonRootHierarchicalDataSourceView : System.Web.UI.HierarchicalDataSourceView
    {
        #region Private Members

        private string parentPath;
        private GetItemHandler itemHandler;
        private GetItemIdHandler itemIdHandler;
        private GetNonRootItemsHandler nonRootHandler;
        private object parentObj;

        #endregion

        #region Constructors

        public NonRootHierarchicalDataSourceView(string parentPath, GetItemHandler itemHandler, 
            GetItemIdHandler itemIdHandler, GetNonRootItemsHandler nonRootHandler)
        {
            this.parentPath = parentPath;
            this.itemHandler = itemHandler;
            this.itemIdHandler = itemIdHandler;
            this.nonRootHandler = nonRootHandler;

            string[] pathSplits = parentPath.Trim(' ', '/').Split('/');
            if (pathSplits != null && pathSplits.Length > 0)
            {
                parentObj = itemHandler(pathSplits[pathSplits.Length - 1]);
            }
        }

        #endregion

        #region Overriden Members

        public override System.Web.UI.IHierarchicalEnumerable Select()
        {
            return new HierarchicalCollection(parentObj, parentPath, 
                nonRootHandler(itemIdHandler(parentObj)), itemIdHandler, nonRootHandler);
        }

        #endregion
    }

    public class SimpleHierarchicalDataSource : System.Web.UI.IHierarchicalDataSource
    {
        #region Private Members

        private GetItemHandler itemHandler;
        private GetItemIdHandler itemIdHandler;
        private GetRootItemsHandler rootHandler;
        private GetNonRootItemsHandler nonRootHandler;

        #endregion

        #region Constructors

        public SimpleHierarchicalDataSource(GetItemHandler itemHandler, GetItemIdHandler itemIdHandler, 
            GetRootItemsHandler rootHandler, GetNonRootItemsHandler nonRootHandler)
        {
            this.itemHandler = itemHandler;
            this.itemIdHandler = itemIdHandler;
            this.rootHandler = rootHandler;
            this.nonRootHandler = nonRootHandler;

            if (DataSourceChanged != null)
            {
                //null is ok
            }
        }

        #endregion

        #region IHierarchicalDataSource Members

        public event EventHandler DataSourceChanged;

        public System.Web.UI.HierarchicalDataSourceView GetHierarchicalView(string viewPath)
        {
            if (viewPath == null || (viewPath != null && (viewPath.Trim().Equals("/") || viewPath.Trim().Equals(""))))
            {
                return new RootHierarchicalDataSourceView(itemIdHandler, rootHandler, nonRootHandler);
            }
            else
            {
                return new NonRootHierarchicalDataSourceView(viewPath, itemHandler, itemIdHandler, nonRootHandler);
            }
        }

        #endregion

        #region Sample Client Handlers

        //public class EntityWrapper<T>
        //{
        //    public T Target;

        //    public EntityWrapper(T obj)
        //    {
        //        Target = obj;
        //    }

        //    public override string ToString()
        //    {
        //        if (typeof(T) == typeof(Entity.Project))
        //        {
        //            return StrongTyped<Entity.Project>(Target).Title;
        //        }
        //        else
        //        {
        //            return StrongTyped<Entity.Requirement>(Target).Title;
        //        }
        //    }
        //}

        //private object[] GetProjects()
        //{
        //    Entity.Project[] projects = Facade.SelectAll<Entity.Project>();
        //    if (projects != null && projects.Length > 0)
        //    {
        //        object[] objs = new object[projects.Length];
        //        for (int i = 0; i < objs.Length; i++)
        //        {
        //            objs[i] = new EntityWrapper<Entity.Project>(projects[i]);
        //        }
        //        return objs;
        //    }
        //    return null;
        //}

        //private object[] GetRequirements(string parentId)
        //{
        //    Entity.Requirement[] reqs;
        //    if (parentId.StartsWith("p"))
        //    {
        //        reqs = Facade.Select<Entity.Requirement>("ParentID = 0 and ProjectID = @ProjectID", int.Parse(parentId.TrimStart('p')));
        //    }
        //    else
        //    {
        //        reqs = Facade.Select<Entity.Requirement>("ParentID = @ParentID", int.Parse(parentId.TrimStart('r')));
        //    }
        //    if (reqs != null && reqs.Length > 0)
        //    {
        //        object[] objs = new object[reqs.Length];
        //        for (int i = 0; i < objs.Length; i++)
        //        {
        //            objs[i] = new EntityWrapper<Entity.Requirement>(reqs[i]);
        //        }
        //        return objs;
        //    }
        //    return null;
        //}

        //private object GetProjectOrRequirement(string id)
        //{
        //    if (id.StartsWith("p"))
        //    {
        //        return new EntityWrapper<Entity.Project>(Facade.Get<Entity.Project>(int.Parse(id.TrimStart('p'))));
        //    }
        //    else
        //    {
        //        return new EntityWrapper<Entity.Requirement>(Facade.Get<Entity.Requirement>(int.Parse(id.TrimStart('p'))));
        //    }
        //}

        //private string GetProjectOrRequirementId(object obj)
        //{
        //    if (obj is EntityWrapper<Entity.Project>)
        //    {
        //        return "p" + StrongTyped<EntityWrapper<Entity.Project>>(obj).Target.ID.ToString();
        //    }
        //    else
        //    {
        //        return "r" + StrongTyped<EntityWrapper<Entity.Requirement>>(obj).Target.ID.ToString();
        //    }
        //}

        #endregion
    }
}